<?php

namespace app\command;

use Carbon\Carbon;
use Exception;
use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Input\InputArgument;
use Symfony\Component\Console\Output\OutputInterface;
use think\facade\Db;
use think\helper\Str;


class MakeModel extends Command
{
    protected static $defaultName = 'make:model';
    protected static $defaultDescription = '创建数据模型';

    protected $modelPath;

    public function __construct () {
        parent::__construct();
        $this->modelPath = base_path('model') . '/';
    }


    /**
     * @return void
     */
    protected function configure()
    {
        $this->addArgument('table_name', InputArgument::REQUIRED, '数据表名称')
            ->addOption('pk', null, InputArgument::OPTIONAL, '主键字段', 'id');
    }

    /**
     * @param InputInterface $input
     * @param OutputInterface $output
     * @return int
     */
    protected function execute(InputInterface $input, OutputInterface $output): int {
        $tableName = $input->getArgument('table_name');
        if (empty($tableName)) {
            $output->writeln('<error>表名不能为空</error>');
            return self::FAILURE;
        }

        // 查询表信息
        try {
            $tableInfo = Db::table($tableName)->getFieldsType();
        } catch (Exception $e) {
            $output->writeln('<error>' . $e->getMessage() . '</error>');
            return self::FAILURE;
        }
        // 生成模型名称及模型文件路径
        $modelName = Str::studly($tableName) . 'Model';
        $modelFile = $this->modelPath . $modelName . '.php';
        // 模型存在则更新
        if (file_exists($modelFile)) {
            $this->updateSchema($tableInfo, $modelName, $modelFile, $output);
            return self::SUCCESS;
        }

        $fields = array_keys($tableInfo);

        // 校验主键
        if (!in_array('id', $fields) && empty($input->getOption('pk'))) {
            $output->writeln('<error>请指定:' . $modelName . '模型主键字段(例如: --pk uid)</error>');
            return self::SUCCESS;
        }

        // 创建模型
        $this->createModel($tableName, $tableInfo, $modelName, $modelFile);
        $output->writeln('<info>模型:' . $modelName . '创建成功, 文件位置: ' . $modelFile . '</info>');
        return self::SUCCESS;
    }

    // 更新模型schema
    protected function updateSchema ($tableInfo, $modelName, $modelPath, OutputInterface $output) {
        $content = file_get_contents($modelPath);
        preg_match('/([\s\S]*)(protected\s+\$schema\s*=\s*\[)([\w\W]*?)(\];)([\s\S]*)/m', $content, $matches);
        $schema = $this->getSchema($tableInfo);
        if (empty($matches)) { // 没有匹配到就创建schema
            preg_match("/([\s\S]*)(protected\s+\\\$table\s*=.*)([\s\S]*)/", $content, $nameMatches);
            if (empty($nameMatches) || count($nameMatches) < 4) {
                $output->writeln('<error>模型:' . $modelName . '数据不完整</error>');
                return;
            }
            $newContent = <<<EOL
{$nameMatches[1]}
    {$nameMatches[2]}
    {$schema}
{$nameMatches[3]}
EOL;
        } else { // 更新schema
            if (count($matches) < 6) {
                $output->writeln('<error>模型:' . $modelName . '数据不完整</error>');
                return;
            }
            $newContent = <<<EOL
{$matches[1]}
    {$schema}
{$matches[5]}
EOL;
        }
        file_put_contents($modelPath, $newContent);
        $output->writeln('<info>模型:' . $modelName . ' schema更新成功</info>');
    }

    // 获取固定长度的空格
    protected function createSpace ($num = 4): string {
        return str_pad('', $num);
    }

    // 获取模型schema
    protected function getSchema ($tableInfo): string {
        $schema = 'protected $schema = [' . PHP_EOL;
        $fields = array_keys($tableInfo);
        $lengths = array_map('strlen', $fields);
        // 最长字符再加两个单引号的长度
        $max = max($lengths) + 2;
        foreach ($tableInfo as $field => $type) {
            // 代码对齐风格追加空格
            $field = str_pad('\'' . $field . '\'', $max);
            $schema .= $this->createSpace(8) . $field . ' => ' . '\'' . $type . '\',' . PHP_EOL;
        }
        $schema .= $this->createSpace() . '];';
        return $schema;
    }

    // 创建模型
    protected function createModel ($tableName, $tableInfo, $modelName, $modelPath) {
        $softDelete = '';
        $deleteTime = '';
        $hidden = '';
        $fields = array_keys($tableInfo);

        // 是否需要软删除
        if (in_array('delete_time', $fields)) {
            $softDelete = 'use SoftDelete;';
            $deleteTime = 'protected $deleteTime = \'delete_time\';';
        }

        // 是否需要自动写入创建时间
        if (in_array('create_time', $fields)) {
            $createTime = 'protected $createTime = \'create_time\';';
        } else {
            $createTime = 'protected $createTime = false;';
        }

        // 是否自动写入更新时间
        if (in_array('update_time', $fields)) {
            $updateTime = 'protected $updateTime = \'update_time\';';
        } else {
            $updateTime = 'protected $updateTime = false;';
        }

        // 设置默认隐藏字段
        if (in_array('delete_time', $fields)) {
            $hidden = 'protected $hidden = [\'delete_time\'];';
        }

        $schema = $this->getSchema($tableInfo);

        $date = Carbon::now()->toDateString();
        $time = Carbon::now()->toTimeString();

        $content = <<<EOL
<?php
/**
 * Created by ModelCreater.
 * Date: {$date}
 * Time: {$time}
 */
namespace model;

use lib\\common\\CommonModel;
use think\\model\\concern\\SoftDelete;

class {$modelName} extends CommonModel 
{    
    {$softDelete}
    
    protected \$table = '{$tableName}';
    {$deleteTime}
    protected \$autoWriteTimestamp = 'datetime';
    {$createTime}
    {$updateTime}
    {$hidden}
    {$schema}
}
EOL;
        file_put_contents($modelPath, $content);
    }

}
